﻿/* Luiz Felipe Netto - Fundamentos de Computação Gráfica
*  Trabalho 1: Cor
*
* Atalhos:
* 1 - XYZ
* 2 - XYZ Normalizado
* 3 - RGB
* 4 - sRGB
* 5 - Lab (L como eixo Y)
* Q - Alterna entre GL_FILL e GL_LINES
*
* Para navegar na tela utilize as teclas WASD e Espaço/X com o mouse.
*
* O trabalho foi desenvolvido em cima de um código base para este e outros trabalhos
* em OpenGL ao longo do semestre (2014/1), base a qual será constantemente aperfeiçoada.
* O projeto completo (vs2013 sem libs) e atualizado pode ser acessado em:
*   https://code.google.com/p/rendering-base/source/browse/ 
* este trabalho se encontr no branch FCG_T1.
*/
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <ctime>

#include <GL\glew.h>
#include <GLFW\glfw3.h>

#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>

#include "manager\callbacks.h"
#include "manager\FramesControl.h"
#include "manager\Mouse.h"
#include "manager\Camera.h"
#include "glsl\Glsl.h"
#include "glsl\Structs.h"
#include "glsl\Grid.h"
#include "glsl\Sphere.h"

#include "fcg\FCG_T1.h"
#include "fcg\Gamut.h"

using namespace glm;

Mouse *mouse;
Camera *camera;
int width = 800;
int height = 600;


// General Init Functions 
GLFWwindow* glfwCfgInit (void);
void glfwCbSetup (GLFWwindow* window);
void openglCfgInit ();
void generalInit ();
void openglOldCfgIni ();

// Draw functions
void drawOldPipeline ();
void drawNewPipeline ();


//Scene 0 Objects
Glsl *p = NULL;

//FGC
void initFCG (void);
FCG_T1 *t1 = NULL; //classe principal 
vec3* pset = NULL; //gamut sendo renderizado
vec3* colorSet = NULL; //referencia de cor para os gamuts
ivec2 setSize = ivec2(0); 
bool labAct = false; //desenha os eixos coordenados para todos gamuts menos lab
bool gFill = true;   // ativa e desativa glFill

int main (void)
{
   srand (1000);
   GLFWwindow* window = NULL;
   generalInit (); // Mouse, Camera and other global objects
   window = glfwCfgInit (); //Init GLFW and OGL Context
   openglCfgInit ();
   openglOldCfgIni ();
   glfwCbSetup (window);
   glfwSetCursorPos (window, width / 2.0, height / 2.0);

   FramesControl *fps = new FramesControl (60, window);
   p = new Glsl ("src/shader/adsPerFrag.vert", NULL, "src/shader/adsPerFrag.frag");
   p->link ();

   initFCG ();

   //MainLoop   
   while (!glfwWindowShouldClose (window)) {

      if (fps->update (width, height) == true) {

         glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
         glfwGetFramebufferSize (window, &width, &height);

         camera->update ();

         drawOldPipeline ();
         drawNewPipeline ();

         glfwSwapBuffers (window);
         glfwPollEvents ();
      }
   }

   glfwDestroyWindow (window);
   glfwTerminate ();

   exit (EXIT_SUCCESS);
}

void initFCG ()
{
   t1 = new FCG_T1 ();
   t1->buildXYZVisibleGamut ();
   pset = t1->getRGBSet ();
   colorSet = t1->getSRGBSet ();
   setSize = t1->getSetSize ();
}

// Desenha os gamuts
void drawSceneFCG ()
{
   if (labAct == false) {
      glBegin (GL_LINES);
      glColor3d (1, 0, 0);
      glVertex3d (0, 0, 0);
      glVertex3d (1, 0, 0);

      glColor3d (0, 1, 0);
      glVertex3d (0, 0, 0);
      glVertex3d (0, 1, 0);

      glColor3d (0, 0, 1);
      glVertex3d (0, 0, 0);
      glVertex3d (0, 0, 1);
      glEnd ();
   }   

   glBegin (GL_QUADS);
   glPointSize (2.0f);
   for (int i = -1; i < setSize.x - 1; i++) {
      for (int j = 0; j < setSize.y - 1; j++) {
         int index = i + j*setSize.x;

         if (i == -1) { // necessário qdo delta > 1            
            glColor3f (0, 0, 0);
            glVertex3f (0, 0, 0);

            glColor3f (0, 0, 0);
            glVertex3f (0, 0, 0);
         }
         else {
            glColor3fv (&colorSet[index][0]);
            glVertex3fv (&pset[index][0]);

            index = i + (j + 1)*setSize.x;
            glColor3fv (&colorSet[index][0]);
            glVertex3fv (&pset[index][0]);
         }

         index = (i + 1) + (j + 1)*setSize.x;
         glColor3fv (&colorSet[index][0]);
         glVertex3fv (&pset[index][0]);

         index = (i + 1) + j*setSize.x;
         glColor3fv (&colorSet[index][0]);
         glVertex3fv (&pset[index][0]);         
      }
   }
   glEnd ();

}

void drawOldPipeline ()
{
   glViewport (0, 0, width, height);
   int vp[4];
   glGetIntegerv (GL_VIEWPORT, vp);
   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   glMatrixMode (GL_PROJECTION);
   glLoadMatrixf (&camera->getProjection ()[0][0]);  

   glMatrixMode (GL_MODELVIEW);

   glLoadMatrixf (&camera->getTransform ()[0][0]); //Load ModelView 
   drawSceneFCG ();

   glLoadMatrixf (&camera->getTransform ()[0][0]); //Load ModelView 
}

void drawNewPipeline ()
{
   p->useProgram (true);
   p->setUniform ("projection", camera->getProjection ());
   p->setUniform ("modelView", camera->getTransform ());
   //gamutXYZN->draw ();
   //p->setUniform ("modelView", camera->getTransform ()*translate (vec3 (2.0f, 0.0f, 0.0f)));
   //gamutXYZ->draw ();
   p->useProgram (false);
}

// General Init Functions 
void generalInit ()
{
   camera = new Camera ((float)width, (float)height);
   mouse = new Mouse ();
}
GLFWwindow*  glfwCfgInit (void)
{
   GLFWwindow* window;

   glfwSetErrorCallback (errorCallback);

   if (glfwInit () == false) {
      exit (EXIT_FAILURE);
   }
   glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 4); // Need to be at least OpenGL 3.3
   glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 4);
   glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);

   window = glfwCreateWindow (width, height, "Simple example", NULL, NULL);
   if (!window) {
      glfwTerminate ();
      std::cout << "Error creating glfw window" << std::endl;
      system ("pause");
      exit (EXIT_FAILURE);
   }

   glfwMakeContextCurrent (window);
   //glfwSetInputMode (window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
   glfwSetWindowPos (window, 20, 50);
   return window;
}
void glfwCbSetup (GLFWwindow* window)
{
   glfwSetCursorPosCallback (window, mouseCursorCallback);
   glfwSetMouseButtonCallback (window, mouseCallback);
   glfwSetKeyCallback (window, keyCallback);
   glfwSetErrorCallback (errorCallback);
   glfwSetWindowSizeCallback (window, windowSizeCallback);
   glfwSetFramebufferSizeCallback (window, framebufferSizeCallback);
}
void openglCfgInit ()
{
   glewExperimental = true;
   GLenum err = glewInit ();
   if (GLEW_OK != err) {
      fprintf (stderr, "Error: %s\n", glewGetErrorString (err));
   }
   fprintf (stdout, "Status: Using GLEW %s\n", glewGetString (GLEW_VERSION));

   const GLubyte *renderer = glGetString (GL_RENDERER);
   const GLubyte *vendor = glGetString (GL_VENDOR);
   const GLubyte *version = glGetString (GL_VERSION);
   const GLubyte *glslVersion =
   glGetString (GL_SHADING_LANGUAGE_VERSION);
   GLint major, minor;
   glGetIntegerv (GL_MAJOR_VERSION, &major);
   glGetIntegerv (GL_MINOR_VERSION, &minor);
   printf ("GL Vendor : %s\n", vendor);
   printf ("GL Renderer : %s\n", renderer);
   printf ("GL Version (string) : %s\n", version);
   printf ("GL Version (integer) : %d.%d\n", major, minor);
   printf ("GLSL Version : %s\n", glslVersion);

   glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
   glEnable (GL_DEPTH_TEST);
   glEnable (GL_BLEND);
   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);

}
void openglOldCfgIni ()
{
   //GLfloat white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
   glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
   glClearColor (0, 0, 0, 0);
}

// Callback Functions ~ //Just a temporary approach
static void errorCallback (int error, const char* description)
{
   fputs (description, stderr);
}
static void keyCallback (GLFWwindow* window, int key, int scancode, int action, int mods)
{
   bool update = true;
   switch (key) {
      case GLFW_KEY_ESCAPE:
         if (action == GLFW_RELEASE) {
            glfwSetWindowShouldClose (window, GL_TRUE);
         }
         break;
      case GLFW_KEY_W:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->FWD);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->FWD);
         }
         break;
      case GLFW_KEY_S:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->BWD);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->BWD);
         }
         break;
      case GLFW_KEY_A:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->STL);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->STL);
         }
         break;
      case GLFW_KEY_D:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->STR);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->STR);
         }
         break;
      case GLFW_KEY_SPACE:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->UP);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->UP);
         }
         break;
      case GLFW_KEY_X:
         if (action == GLFW_PRESS) {
            camera->setMovDir (camera->DOWN);
         }
         else if (action == GLFW_RELEASE) {
            camera->unSetMovDir (camera->DOWN);
         }
         break;
      case GLFW_KEY_1:
         if (action == GLFW_PRESS) {
            pset = t1->getXYZSet ();
            labAct = false;
         }
         break;
      case GLFW_KEY_2:
         if (action == GLFW_PRESS) {
            pset = t1->getXYZNSet ();
            labAct = false;
         }
         break;
      case GLFW_KEY_3:
         if (action == GLFW_PRESS) {
            pset = t1->getRGBSet ();
            labAct = false;
         }
         break;
      case GLFW_KEY_4:
         if (action == GLFW_PRESS) {
            pset = t1->getSRGBSet ();
            labAct = false;
         }
         break;
      case GLFW_KEY_5:
         if (action == GLFW_PRESS) {
            pset = t1->getLabSet ();
            labAct = true;
         }
         break;
      case GLFW_KEY_Q:
         if (action == GLFW_RELEASE) {
            if (gFill == true) {
               glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
               gFill = false;
            }
            else {
               glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
               gFill = true;
            }
         }
         break;
      default:
         update = false;
         break;
   }
   if (update == false) {
      if (action == GLFW_RELEASE) {
         printf ("%c Released\n", key);
      }
      else {
         printf ("%c Pressed\n", key);
      }
   }
   else {
      //printf (" %c: Pos( %.2f, %.2f, %.2f )\n", key, camera->getPosition ().x, camera->getPosition ().y, camera->getPosition ().z);
   }
}
static void mouseCallback (GLFWwindow* window, int button, int action, int mods)
{
   mouse->setButton (button, action, mods);
   printf ("(%.2lf, %.2lf) bt:%d act:%d mod:%d\n", mouse->getPos ().x, mouse->getPos ().y, mouse->getButton (), mouse->getAction (), mouse->getMods ());
}
static void mouseCursorCallback (GLFWwindow* window, double px, double py)
{
   //printf ("x: %.2lf y: %.2lf\n", px, py);
   mouse->setPos (px, py);
   camera->setMouse (vec2 (px, py));
}
static void windowSizeCallback (GLFWwindow *window, int width, int height)
{
   std::cout << "Entrou " << width << " " << height << std::endl;
   //glfwSetWindowSize (window, width, height);
   camera->setDimension ((float)width, (float)height);
}
static void framebufferSizeCallback (GLFWwindow* window, int width, int height)
{
   glViewport (0, 0, width, height);
}
